﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;

namespace NovoGenus.Sockets.Http.Headers
{
    public class HttpResponseHeaders 
    {
        private bool _headerFound = false;

        public List<DictionaryEntry> Headers { get; set; }

        public int ResponseCode { get; set; }
        public int Age { get; set; }
        public int ContentLength { get; set; }

        public string Server { get; set; }
        public string ContentType { get; set; }
        public string TransferEncoding {get;set;}
        public string Connection { get; set; }
        public string SetCookie { get; set; }
        public string CacheControl { get; set; }
        public string ETag { get; set; }
        public string XPoweredBy { get; set; }
        public string AccessControlAllowOrigin { get; set; }
        public string CFCacheStatus { get; set; }
        public string CFRAY { get; set; } 
        public string AcceptRanges { get; set; }
        public string Host { get; set; }

        public DateTime Expires { get; set; }
        public DateTime LastModified { get; set; }
        public DateTime ResponseDate { get; set; }




        public HttpResponseHeaders()
        {
            TransferEncoding = string.Empty;
            Headers = new List<DictionaryEntry>();
        }

        private DateTime GetResponseLineDate(string date)
        {
            string[] lines = GetHeaderResponseLine(date);
            DateTime rd = DateTime.Now;
            if (!DateTime.TryParse(lines[1], out rd)) rd = DateTime.Now.AddDays(-1);
            return rd;
        }


        public string[] GetHeaderResponseLine(string line)
        {
            string[] lines = new string[2];
            int colonI = line.IndexOf(':');
            if (colonI > 0)
            {
                lines[0] = line.Substring(0, colonI).Trim();
                lines[1] = line.Substring(colonI + 1, line.Length - colonI - 1).Trim();
            }
            else
            {
                lines[0] = line;
                lines[1] = string.Empty;
            }
           
            return lines;
        }

        private int GetResponseInt(string line)
        {
            int i = 0;
            int.TryParse(line, out i);
            return i;
        }


        public int ParseResponseData(ref byte[] buffer)
        {
            int bufferLengthToReadNext = 0;
            string responseData = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
            string[] headers = null;
            if (!_headerFound) headers = responseData.Split('\n');
            else {
                headers = responseData.Split(new char[]{'\r','\n'},StringSplitOptions.RemoveEmptyEntries);
            }


            for (int i = 0; i < headers.Length; i++)
            {
                if (!_headerFound)
                {
                    string[] responseLine = GetHeaderResponseLine(headers[i]);
                    if (responseLine[0].Equals("\r"))
                    {
                        _headerFound = true;
                    }
                    else
                    {
                        AddHeader(responseLine[0], responseLine[1]);
                    }
                }
                else
                {
                   // Body+= 
                }

            }
            

            return bufferLengthToReadNext;
        }


        public void AddHeader(string hKey, string hValue)
        {
            if (!string.IsNullOrEmpty(hKey)) 
            { 
                switch (hKey)
                {
                    case "Date":
                        ResponseDate = GetResponseLineDate(hValue);
                        Headers.Add(new DictionaryEntry("Date", ResponseDate));
                        break;
                    case "Server":
                        Server = hValue;
                        Headers.Add(new DictionaryEntry("Server", Server));
                        break;
                    case "Content-Type":
                        ContentType = hValue;
                        Headers.Add(new DictionaryEntry("Content-Type", ContentType));
                        break;
                    case "Transfer-Encoding":
                        TransferEncoding = hValue;
                        Headers.Add(new DictionaryEntry("Transfer-Encoding", ContentType));
                        break;
                    case "Connection":
                        Connection = !string.IsNullOrEmpty(Connection)?string.Concat(Connection, "; ", hValue):hValue;;
                        Headers.Add(new DictionaryEntry("Connection", Connection));
                        break;
                    case "Set-Cookie":
                        SetCookie = hValue;
                        Headers.Add(new DictionaryEntry("Set-Cookie", SetCookie));
                        break;
                    case "Cache-Control":
                        CacheControl = hValue;
                        Headers.Add(new DictionaryEntry("Cache-Control", CacheControl));
                        break;
                    case "Last-Modified":
                        LastModified = GetResponseLineDate(hKey);
                        Headers.Add(new DictionaryEntry("Last-Modified", LastModified));
                        break;
                    case "ETag":
                        ETag = hValue;
                        Headers.Add(new DictionaryEntry("ETag", ETag));
                        break;
                    case "X-Powered-By":
                        XPoweredBy = hValue;
                        Headers.Add(new DictionaryEntry("X-Powered-By", XPoweredBy));
                        break;
                    case "Access-Control-Allow-Origin":
                        AccessControlAllowOrigin = hValue;
                        Headers.Add(new DictionaryEntry("Access-Control-Allow-Origin", AccessControlAllowOrigin));
                        break;
                    case "CF-Cache-Status":
                        CFCacheStatus = hValue;
                        Headers.Add(new DictionaryEntry("CF-Cache-Status", CFCacheStatus));
                        break;
                    case "Expires":
                        Expires = GetResponseLineDate(hKey);
                        Headers.Add(new DictionaryEntry("Expires", Expires));
                        break;
                    case "CF-RAY":
                        CFRAY = hValue;
                        Headers.Add(new DictionaryEntry("CF-RAY", CFRAY));
                        break;
                    case "Content-Length":
                        ContentLength = GetResponseInt(hValue);
                        Headers.Add(new DictionaryEntry("Content-Length", ContentLength));
                        break;
                    case "Accept-Ranges":
                        AcceptRanges = hValue;
                        Headers.Add(new DictionaryEntry("Accept-Ranges", AcceptRanges));
                        break;
                    case "Host":
                        Host = hValue;
                        Headers.Add(new DictionaryEntry("Host", Host));
                        break;
                    case "Age":
                        Age = GetResponseInt(hValue);
                        Headers.Add(new DictionaryEntry("Age", Age));
                        break;
                    default:
                        if (hKey.Contains("HTTP/")) //it's the response code
                        {
                            int responseCode;
                            if (int.TryParse(hKey.Split(' ')[1], out responseCode))
                            {
                                ResponseCode = responseCode;
                                Headers.Add(new DictionaryEntry("Response Code", responseCode));
                            }
                        }
                        Headers.Add(new DictionaryEntry(hKey, hValue));
                        break;
                }
            }

        }

        public void ParseHeaderResponse(byte[] rawHeader, int eol)
        {
            string headers = Encoding.UTF8.GetString(rawHeader, 0, eol);
            char[] sepearator = new char[] { '\r', '\n' };
            string[] headerLines = headers.Split(sepearator, StringSplitOptions.RemoveEmptyEntries);

            int lineCount = headerLines.Length;
            if (lineCount > 0)
            {
                //rules; first line is always the response code; i.e. HTTP/1.1 200 OK
                int responseCode;
                if (int.TryParse(headerLines[0].Split(' ')[1], out responseCode))
                {
                    ResponseCode = responseCode;
                    Headers.Add(new DictionaryEntry("Response Code", responseCode));
                    for (int i = 1; i < lineCount; i++)
                    {
                        string[] headline = GetHeaderResponseLine(headerLines[i]);
                        switch (headline[0])
                        {
                            case "Date":
                                ResponseDate = GetResponseLineDate(headerLines[i]);
                                Headers.Add(new DictionaryEntry("Date", ResponseDate));
                                break;
                            case "Server":
                                Server = headline[1];
                                Headers.Add(new DictionaryEntry("Server", Server));
                                break;
                            case "Content-Type":
                                ContentType = headline[1];
                                Headers.Add(new DictionaryEntry("Content-Type", ContentType));
                                break;
                            case "Transfer-Encoding":
                                TransferEncoding = headline[1];
                                Headers.Add(new DictionaryEntry("Transfer-Encoding", ContentType));
                                break;
                            case "Connection":
                                Connection = headline[1];
                                Headers.Add(new DictionaryEntry("Connection", Connection));
                                break;
                            case "Set-Cookie":
                                SetCookie = headline[1];
                                Headers.Add(new DictionaryEntry("Set-Cookie", SetCookie));
                                break;
                            case "Cache-Control":
                                CacheControl = headline[1];
                                Headers.Add(new DictionaryEntry("Cache-Control", CacheControl));
                                break;
                            case "Last-Modified":
                                LastModified = GetResponseLineDate(headerLines[i]);
                                Headers.Add(new DictionaryEntry("Last-Modified", LastModified));
                                break;
                            case "ETag":
                                ETag = headline[1];
                                Headers.Add(new DictionaryEntry("ETag", ETag));
                                break;
                            case "X-Powered-By":
                                XPoweredBy = headline[1];
                                Headers.Add(new DictionaryEntry("X-Powered-By", XPoweredBy));
                                break;
                            case "Access-Control-Allow-Origin":
                                AccessControlAllowOrigin = headline[1];
                                Headers.Add(new DictionaryEntry("Access-Control-Allow-Origin", AccessControlAllowOrigin));
                                break;
                            case "CF-Cache-Status":
                                CFCacheStatus = headline[1];
                                Headers.Add(new DictionaryEntry("CF-Cache-Status", CFCacheStatus));
                                break;
                            case "Expires":
                                Expires = GetResponseLineDate(headerLines[i]);
                                Headers.Add(new DictionaryEntry("Expires", Expires));
                                break;
                            case "CF-RAY":
                                CFRAY = headline[1];
                                Headers.Add(new DictionaryEntry("CF-RAY", CFRAY));
                                break;
                            case "Content-Length":
                                ContentLength = GetResponseInt(headline[1]);
                                Headers.Add(new DictionaryEntry("Content-Length", ContentLength));
                                break;
                            case "Accept-Ranges":
                                AcceptRanges = headline[1];
                                Headers.Add(new DictionaryEntry("Accept-Ranges", AcceptRanges));
                                break;
                            case "Host":
                                Host = headline[1];
                                Headers.Add(new DictionaryEntry("Host", Host));
                                break;
                            case "Age":
                                Age = GetResponseInt(headline[1]);
                                Headers.Add(new DictionaryEntry("Age", Age));
                                break;
                            default:
                                Headers.Add(new DictionaryEntry(headline[0], headline[1]));
                                break;
                        }
                    }
                }
                else
                {
                    throw new NotSupportedException();
                }
            }
        }

   
    }
}
